import { GameRouteData, routeApiConfig } from "core/utils/localApi";
import mongodbHandle from "./mongodbHandle"
import playerHandle from "core/handle/playerHandle";
import app from "src/app";
var ObjectId = require('mongodb').ObjectId;
import moment = require('moment');
import Random from "core/utils/random";
import route from "core/route/route";
import configFile from '../config';
import redisHandle from "./redisHandle";
import routes from "src/route/route";
import sessionHandle from "./sessionHandle";
import { PlayerModel } from "core/model/playerModel";
import bagHandle from "core/handle/bagHandle";
import adventureHandle from "core/handle/adventureHandle";
import { gameConfig } from "core/config/gameConfig";

class PlayerSessionHandle {
    players: any
    playerAmount: number
    constructor() {
        this.players = {}
        this.playerAmount = 0
    }
    init = async () => {
        routeApiConfig.isClient = false
        await redisHandle.init('playerSessionHandle')
        await mongodbHandle.init('playerSessionHandle')
        process.on('message', async (msg) => {
            let url = msg.url
            if (url == "updateAllPlayer") {
                console.log("updateAllPlayer" + app.forkID)
                let playerAmount = await this.updatePlayer()
                console.log("updateAllPlayer end" + app.forkID + " playerAmount:" + playerAmount)
                process.send({ messageID: msg.messageID, data: { err: 0 } })
                return
            } else if (url == "updatePlayerYearVip") {
                this.updatePlayerYearVip(msg.data)
                process.send({ messageID: msg.messageID, data: { err: 0 } })
                return
            }
            let session = await sessionHandle.getSession(msg.sessionKey)
            if (!session) {
                if (msg.messageID) {
                    process.send({ messageID: msg.messageID, data: { err: 101, code: 0 } })
                }
                return
            }
            if (url == "route") {
                this.routeData(msg, session)
            } else if (url == "routeDatas") {
                this.routeDatas(msg, session)
            } else {
                let loginRoute = routes.login[url];
                if (loginRoute) {
                    try {
                        let { err, data, code } = await loginRoute(msg.data, session)
                        if (msg.messageID) {
                            process.send({ messageID: msg.messageID, data: { err, data, code } })
                        }
                    } catch (e) {
                        mongodbHandle.errorDao.newAndSave(session.pID, 2, e.message, "loginRoute", url)
                        process.send({ messageID: msg.messageID, data: { err: 125, code: 0, data: e.message } })
                    }
                }
            }
        })
        setInterval(() => {
            this.updatePlayer()
        }, 2 * 60 * 1000)

    }
    routeData = async (msg, session) => {
        let routeDatas = msg.data
        if (routeDatas.rName == "cheatRoute" && routeDatas.testclient !== "cheatqihuan234") {
            process.send({ messageID: msg.messageID, data: { err: 121, code: 0 } })
            return
        }
        let routeData = routeDatas.data
        if (routeDatas.fName == "getMail") {
            if (routeData && routeData[0]) {
                let mailid = routeData[0]
                let doc = await mongodbHandle.mailDao.findOne({ _id: ObjectId(mailid), pID: session.pID })
                if (!doc || doc.state == 2) {
                    process.send({ messageID: msg.messageID, data: { err: 122, code: 0 } })
                    return
                } else {
                    routeData[0] = doc
                }
                await mongodbHandle.mailDao.updateOne({ _id: ObjectId(mailid) }, { $set: { state: 2 } })
            } else {
                process.send({ messageID: msg.messageID, data: { err: 123, code: 0 } })
                return
            }

        }
        let playerSessionData = playerSessionHandle.getPlayer(session.pID)
        if (!playerSessionData) {
            playerSessionData = await playerSessionHandle.addPlayer(session.pID)
            playerSessionData.requestIndex = routeDatas.index
        }
        if (playerSessionData.requestIndex !== routeDatas.index) {
            process.send({ messageID: msg.messageID, data: { err: 124, data: playerSessionData.requestIndex } })
            return
        }
        if (playerSessionData) {
            let date = new Date()
            let time = date.getTime()
            let player: PlayerModel = playerSessionData.data.playerData
            let dayTime = moment().startOf('day').valueOf()
            try {
                let { vipLvl } = playerHandle.getVip(player)

                let gameRouteData: GameRouteData = {
                    player: player,
                    random: new Random(routeDatas.s),
                    time: routeDatas.t, dayTime,
                    vipLvl: vipLvl,
                    isMonthVip: (playerSessionData.data.vipTime || 0) > time,
                    isYearVip: playerSessionData.data.vip2,
                    version: app.version
                }
                let { err } = await route[routeDatas.rName][routeDatas.fName](...routeData, gameRouteData)
                if (err == 0) {
                    playerSessionData.data.playerData = gameRouteData.player
                    playerSessionData.time = new Date().getTime()
                    playerSessionData.amount += 1
                    playerSessionData.requestIndex += 1
                    playerSessionData.isSave = false
                    if (playerSessionData.amount >= 20) {
                        playerSessionData.isSave = true
                        playerSessionData.amount = 0

                        if (this.players[session.pID].bakTime + 1 * 60 * 60 * 1000 < time) {
                            this.players[session.pID].bakTime = time
                            let hour = 'hour' + moment().hour()
                            let playerData = 'playerData' + moment().hour()
                            await mongodbHandle.playerBakDao.updateOne({
                                pID: session.pID
                            }, {
                                $set: {
                                    time: moment().valueOf(),
                                    [hour]: moment().valueOf(),
                                    [playerData]: player
                                }
                            }, { upsert: true })
                        }
                        let nowTime = new Date().getTime()
                        await mongodbHandle.playerDao.updateOne({
                            _id: ObjectId(session.pID),
                        }, {
                            $set: {
                                playerData: gameRouteData.player,
                                loginTime: nowTime,
                                saveTime: nowTime,
                                bakTime: this.players[session.pID].bakTime
                            }
                        })
                    }
                }
                process.send({ messageID: msg.messageID, data: { err } })
            } catch (e) {
                let rName = routeDatas.rName
                let fName = routeDatas.fName
                mongodbHandle.errorDao.newAndSave(session.pID, 1, e.message, rName, fName)
                process.send({ messageID: msg.messageID, data: { err: 120, code: 0, data: e.message } })
            }
        }
    }
    routeDatas = async (msg, session) => {
        for (let m = 0; m < msg.data.data.length; m++) {
            let routeDatas = msg.data.data[m]
            if (routeDatas.rName == "cheatRoute" && msg.data.testclient !== "cheatqihuan234") {
                process.send({ messageID: msg.messageID, data: { err: 121, code: 0 } })
                return
            }
            let routeData = routeDatas.data
            if (routeDatas.fName == "getMail") {
                if (routeData && routeData[0]) {
                    let mailid = routeData[0]._id
                    let doc = await mongodbHandle.mailDao.findOne({ _id: ObjectId(mailid), pID: session.pID })
                    if (!doc || doc.state == 2) {
                        process.send({ messageID: msg.messageID, data: { err: 122, code: 0 } })
                        return
                    } else {
                        routeData[0] = doc
                    }
                    await mongodbHandle.mailDao.updateOne({ _id: ObjectId(mailid) }, { $set: { state: 2 } })
                } else {
                    process.send({ messageID: msg.messageID, data: { err: 123, code: 0 } })
                    return
                }
            }
            let playerSessionData = playerSessionHandle.getPlayer(session.pID)
            if (!playerSessionData) {
                playerSessionData = await playerSessionHandle.addPlayer(session.pID)
                playerSessionData.requestIndex = routeDatas.index
            }
            if (playerSessionData.requestIndex !== routeDatas.index) {
                process.send({ messageID: msg.messageID, data: { err: 124, data: playerSessionData.requestIndex } })
                return
            }
            if (playerSessionData) {
                let date = new Date()
                let time = date.getTime()
                let player: PlayerModel = playerSessionData.data.playerData
                let dayTime = moment().startOf('day').valueOf()
                if (routeDatas.d && Math.abs(routeDatas.d - dayTime) < (25 * 60 * 60 * 1000)) {
                    dayTime = routeDatas.d
                }
                if (routeDatas.t && Math.abs(routeDatas.t - time) < (15 * 1000)) {
                    time = routeDatas.t
                }
                try {
                    let { vipLvl } = playerHandle.getVip(player)
                    let gameRouteData: GameRouteData = {
                        player: player,
                        random: new Random(routeDatas.s),
                        time: time, dayTime,
                        vipLvl: vipLvl,
                        isMonthVip: (playerSessionData.data.vipTime || 0) > time,
                        isYearVip: playerSessionData.data.vip2,
                        version: app.version
                    }
                    let { err } = await route[routeDatas.rName][routeDatas.fName](...routeData, gameRouteData)
                    if (err == 0) {
                        playerSessionData.data.playerData = gameRouteData.player
                        playerSessionData.time = new Date().getTime()
                        playerSessionData.amount += 1
                        playerSessionData.requestIndex += 1
                        playerSessionData.isSave = false
                        if (playerSessionData.amount >= 20) {
                            playerSessionData.isSave = true
                            playerSessionData.amount = 0

                            if (this.players[session.pID].bakTime + 1 * 60 * 60 * 1000 < time) {
                                this.players[session.pID].bakTime = time
                                let hour = 'hour' + moment().hour()
                                let playerData = 'playerData' + moment().hour()
                                await mongodbHandle.playerBakDao.updateOne({
                                    pID: session.pID
                                }, {
                                    $set: {
                                        time: moment().valueOf(),
                                        [hour]: moment().valueOf(),
                                        [playerData]: gameRouteData.player
                                    }
                                }, { upsert: true })
                            }
                            let player = gameRouteData.player
                            let nowTime = new Date().getTime()
                            let playerData = this.players[session.pID].data
                            let mapProgressMax = playerData.mapProgressMax
                            if (player && player.adventure) {
                                let { total, maxTotal } = adventureHandle.getAdventureMapProgress(player)
                                mapProgressMax = total
                            }
                            let totalPower = playerData.totalPower
                            if (player && player.baseInfo.power) {
                                totalPower = player.baseInfo.power
                            }
                            let abyssProgress = playerData.abyssProgress
                            if (player) {
                                let mapConfig = gameConfig.map
                                let mapID = mapConfig.abyssMapID
                                let floor = player.adventure.mapProgress[mapID] || 1
                                abyssProgress = floor
                            }
                            await mongodbHandle.playerDao.updateOne({
                                _id: ObjectId(session.pID),
                            }, {
                                $set: {
                                    playerData: gameRouteData.player,
                                    mapProgressMax: mapProgressMax,
                                    totalPower: totalPower,
                                    abyssProgress: abyssProgress,
                                    loginTime: nowTime,
                                    saveTime: nowTime,
                                    bakTime: this.players[session.pID].bakTime
                                }
                            })
                        }
                    }
                    process.send({ messageID: msg.messageID, data: { err } })
                } catch (e) {
                    let rName = routeDatas.rName
                    let fName = routeDatas.fName
                    mongodbHandle.errorDao.newAndSave(session.pID, 1, e.message, rName, fName)
                    process.send({ messageID: msg.messageID, data: { err: 120, code: 0, data: e.message } })
                }
            }
        }
    }
    addPlayer = async (pID) => {
        if (!this.players[pID]) {
            this.playerAmount += 1
        }
        let res = await mongodbHandle.playerDao.findOne({ _id: ObjectId(pID) })
        if (res) {
            this.players[pID] = { data: res, time: new Date().getTime(), amount: 0, isSave: true, bakTime: res.bakTime || new Date().getTime(), requestIndex: 0 }
            this.players[pID].data.playerData = new PlayerModel(this.players[pID].data.playerData)
            return this.players[pID]
        }
    }
    getPlayer = (pID) => {
        return this.players[pID]
    }
    updatePlayerYearVip = async (pID) => {
        let data = this.players[pID]
        if (data) {
            data.data.vip2 = true
        }
    }

    savePlayerAndDelete = async (pID) => {
        let data = this.players[pID]
        if (data && data.isSave == false) {
            let playerData = data.data
            await mongodbHandle.playerDao.updateOne({
                _id: ObjectId(pID),
            }, {
                $set: {
                    playerData: playerData.playerData,
                    loginTime: new Date().getTime(),
                }
            })
            data.isSave = true
            data.amount = 0
            data.time = new Date().getTime()
        }
        if (data) {
            this.deleteByPID(pID)
        }
        return null
    }

    deleteByPID = (pID) => {
        delete this.players[pID]
        this.playerAmount -= 1
    }

    async updatePlayer() {
        let playerAmount = 0
        let pIDs = []
        for (let [pID, data] of Object.entries(this.players)) {
            let _data = data as any
            let time = _data.time
            if (_data.isSave == false) {
                let playerData = _data.data
                let player: PlayerModel = playerData.playerData


                if (this.players[pID].bakTime + 1 * 60 * 60 * 1000 < time) {
                    this.players[pID].bakTime = time
                    let hour = 'hour' + moment().hour()
                    let playerData = 'playerData' + moment().hour()
                    await mongodbHandle.playerBakDao.updateOne({
                        pID: pID
                    }, {
                        $set: {
                            time: moment().valueOf(),
                            [hour]: moment().valueOf(),
                            [playerData]: player
                        }
                    }, { upsert: true })
                }
                let nowTime = new Date().getTime()

                let mapProgressMax = playerData.mapProgressMax
                if (player && player.adventure) {
                    let { total, maxTotal } = adventureHandle.getAdventureMapProgress(player)
                    mapProgressMax = total
                }
                let totalPower = playerData.totalPower
                if (player && player.baseInfo.power) {
                    totalPower = player.baseInfo.power
                }
                let abyssProgress = playerData.abyssProgress
                if (player) {
                    let mapConfig = gameConfig.map
                    let mapID = mapConfig.abyssMapID
                    let floor = player.adventure.mapProgress[mapID] || 1
                    abyssProgress = floor
                }

                await mongodbHandle.playerDao.updateOne({
                    _id: ObjectId(pID),
                }, {
                    $set: {
                        playerData: player,
                        mapProgressMax: mapProgressMax,
                        totalPower: totalPower,
                        abyssProgress: abyssProgress,
                        loginTime: nowTime,
                        saveTime: nowTime,
                        bakTime: this.players[pID].bakTime
                    }
                })
                _data.isSave = true
                _data.amount = 0
            }
            let now = new Date().getTime()
            if (now - time > 10 * 60 * 60 * 24) {
                this.deleteByPID(pID)
                pIDs.push(pID)
            } else {
                playerAmount += 1

            }
        }
        process.send({ message: "playerAmount", playerAmount, forkID: app.forkID, pIDs: pIDs })
        return playerAmount
    }

}

let playerSessionHandle = new PlayerSessionHandle()

export default playerSessionHandle